//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file memfile.h
///
///
/// @author Davide Pasca
///
//==================================================================

#ifndef PUTILS_MEMFILE_H
#define PUTILS_MEMFILE_H

#include "../../psys/include/psys.h"

//==================================================================
namespace PUtils
{

//==================================================================
struct Memfile
{
	bool				_is_static_data;
	bool				_owns_static_data;
	PArraySta<u_char>	_stadatap;
	int					_data_cur_idx;
	std::vector<u_char>	_dyndatap;
	int					_bit_cnt;
	u_long64			_bitstr_tmp;

	//==================================================================
	Memfile() :
		_is_static_data(false),
		_owns_static_data(false),
		_data_cur_idx(0),
		_bit_cnt(0),
		_bitstr_tmp(0)
	{
	}

	//==================================================================
	Memfile( void *datap, u_int size ) :
		_is_static_data(true),
		_owns_static_data(false),
		_data_cur_idx(0),
		_bit_cnt(0),
		_bitstr_tmp(0)
	{
		SetupReadWrite( datap, size );
	}

	//==================================================================
	Memfile( const void *datap, u_int size ) :
		_is_static_data(true),
		_owns_static_data(false),
		_data_cur_idx(0),
		_bit_cnt(0),
		_bitstr_tmp(0)
	{
		SetupRead( datap, size );
	}

	//==================================================================
	Memfile( const Memfile &from ) :
		_is_static_data(true),
		_owns_static_data(false),
		_data_cur_idx(0),
		_bit_cnt(from._bit_cnt),
		_bitstr_tmp(from._bitstr_tmp)
	{
		SetupRead( from.GetData(), from.GetDataSize() );
	}

	//==================================================================
	Memfile( const TCHAR *fnamep );

	~Memfile();

	//==================================================================
	void	SetupReadWrite( void *datap, u_int size )
	{
		_is_static_data = true;
		_stadatap.Setup( (u_char *)datap, size, size );
	}

	//==================================================================
	void	SetupRead( const void *datap, u_int size )
	{
		_is_static_data = true;
		_stadatap.Setup( (const u_char *)datap, size, size );
	}

	//==================================================================
	void	CopyFrom( const Memfile &src ) throw(...);

	void		*GetData()
	{
		return _is_static_data ? _stadatap.begin() :
									(_dyndatap.size() ? &_dyndatap.front() : NULL);
	}
	
	const void	*GetData() const
	{
		return _is_static_data ? _stadatap.begin() :
									(_dyndatap.size() ? &_dyndatap.front() : NULL);
	}

	u_int		GetDataSize() const	{	return _is_static_data ? _stadatap.len() : _dyndatap.size();	}
	void		SetDataSize( u_int new_size )
	{
		if ( _is_static_data )
			_stadatap.resize( new_size );
		else
			_dyndatap.resize( new_size );
	}

	void	WriteInt( int val ) throw(...);
	int		ReadInt() throw(...);

	void	WriteUInt( u_int val ) throw(...);
	u_int	ReadUInt() throw(...);

	void	WriteFloat( float val ) throw(...);
	float	ReadFloat() throw(...);

	void	WriteUShort( u_short val ) throw(...);
	u_short	ReadUShort() throw(...);

	void	WriteUChar( u_char val ) throw(...);
	u_char	ReadUChar() throw(...);
	void	WriteData( const void *datap, u_int size ) throw(...);
	void	ReadData( void *datap, u_int size ) throw(...);
	void	WriteUCharArray( const std::vector<u_char> &array ) throw(...);
	void	ReadUCharArray( std::vector<u_char> &array ) throw(...);
	void	WriteMemfile( const Memfile *memfp ) throw(...);
	void	ReadMemfile( Memfile *memfp ) throw(...);
	bool	ReadCStringPad2( char *dest_strp, int dest_str_max_len ) throw(...);

	void	SeekFromStart( u_int offset ) throw(...);
	void	SeekFromCurPos( int offset ) throw(...);
	void	SeekFromEnd( int offset ) throw(...);
	u_int	CountBytesToEnd() const;
	u_int	GetCurPos() const;
	bool	IsEOF();

	void	WriteBits( u_int val, int nbits )  throw(...)
			{
				_bitstr_tmp = (_bitstr_tmp << nbits) | val;
				_bit_cnt += nbits;

				while ( _bit_cnt >= 8 )
				{
					_bit_cnt -= 8;
					WriteUChar( (u_char)(_bitstr_tmp >> _bit_cnt) );
				}
			}

	u_int	ReadBits( int nbits ) throw(...);
	void	WriteAlignByte() throw(...);
	void	ReadAlignByte() throw(...);
	
private:
	void	tryExpand( int size ) throw(...);

	/*
	Memfile& operator=(const Memfile&)
		{
			PASSERT( 0 );
			return *this;
		}*/
	

	Memfile& operator=(Memfile&)
	{
		PASSERT( 0 );
		return *this;
	}
};

//==================================================================
struct FILEM
{
	Memfile		*_mfp;

	FILEM( Memfile *mfp ) :
		_mfp(mfp)
	{
	}
};

//==================================================================
errno_t mtfopen_s( FILEM ** _File, PUtils::Memfile *mfp, const TCHAR * _Mode );
size_t mfread_s( void *_DstBuf, size_t _DstSize, size_t _ElementSize, size_t _Count, FILEM * _File );
size_t mfread( void *_DstBuf, size_t _ElementSize, size_t _Count, FILEM * _File );
int mfseek( FILEM * _File, long _Offset, int _Origin );
void mfclose( FILEM * _File );
int mfgetc( FILEM * _File );
long mftell( FILEM * _File );



//==================================================================
};

#endif